/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi_cast_engine_dmr_service.h"

#include <cstdint>

#include "cast_engine_common.h"
#include "cast_engine_dlna_service_died_listener.h"
#include "js_native_api.h"
#include "surface_utils.h"
#include "ui/rs_surface_node.h"
#include "window.h"


using namespace OHOS;
using namespace OHOS::CastEngine::CastEngineService;

namespace OHOS {
namespace CastEngine {
namespace CastEngineClient {
DEFINE_CAST_ENGINE_LABEL("Napi-Cast-Engine-Dlna-Service");

using namespace CastEngineService;

napi_value RegisterRemoteDiedListener(napi_env env, napi_callback_info info)
{
    napi_value ret = nullptr;
    napi_create_int32(env, 0, &ret);
    auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgr == nullptr) {
        CLOGW("RegisterRemoteDiedListener erro samgr");
        return nullptr;
    } else {
        CLOGI("RegisterRemoteDiedListener samgr  got");
    }
    sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(1603);
    auto Recipient = new CastEngineDlnaServiceDiedListener();
    if (!remoteObject->AddDeathRecipient(Recipient)) {
        napi_create_int32(env, -1, &ret);
        CLOGI("RegisterRemoteDiedListener add fail");
        return ret;
        
    }
    return ret;
}

sptr<ICastEngineDlnaService> GetAdaptor()
{
    auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgr == nullptr) {
        CLOGW("erro samgr");
        return nullptr;
    } else {
        CLOGW("samgr got");
    }
    sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(1603);
    if (remoteObject != nullptr) {
        CLOGW("Got Dlna Service object");
    } else {
        return nullptr;
    }
    sptr<ICastEngineDlnaService> dlnaService = iface_cast<ICastEngineDlnaService>(remoteObject);
    return dlnaService;
}

int SendDmrPlayer(const sptr<IRemoteObject>& object)
{
    int result = -1;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        return result;
    }
    CLOGI("DlnaClientAppWork SendDmrPlayer");
    result = adaptor->SendDmrPlayer(object);
    return result;
}

napi_value Play(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Play();
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value Pause(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Pause();
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value Stop(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Stop();
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value Forward(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    size_t argc = 1;
    napi_value args = 0;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &args, nullptr, nullptr));
    int ForwardForSeconds = 0;
    napi_get_value_int32(env, args, &ForwardForSeconds);
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Forward(ForwardForSeconds);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value Backward(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    size_t argc = 1;
    napi_value args = 0;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &args, nullptr, nullptr));
    int ForwardForSeconds = 0;
    napi_get_value_int32(env, args, &ForwardForSeconds);
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Backward(ForwardForSeconds);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value GetPosition(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    adaptor->GetPosition(result);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value GetPlayerStatus(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    adaptor->GetPlayerStatus(result);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value GetDuration(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    adaptor->GetDuration(result);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value Seek(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
    int32_t position;
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &position));
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->Seek(position);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value SetSurfaceId(napi_env env, napi_callback_info info)
{
    CLOGW("SetSurfaceId in");
    napi_value args[1];

    char strBuf[256];
    size_t strSize;
    size_t argc = 1;
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], strBuf, sizeof(strBuf), &strSize));
    std::string surfaceId(strBuf, strSize);
    CLOGW("surfaceId:%{public}s", surfaceId.c_str());

    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");

        napi_create_int32(env, result, &ret);
        return ret;
    }
    uint64_t surfaceUniqueId = static_cast<uint64_t>(std::strtoll(surfaceId.c_str(), nullptr, 10));
    if (errno == ERANGE) {
        CLOGE("errno == ERANGE");
        napi_create_int32(env, result, &ret);
        return ret;
    }

    sptr<Surface> surface = SurfaceUtils::GetInstance()->GetSurface(surfaceUniqueId);
    if (!surface) {
        CLOGE("surface is null, surface uniqueId %llu", surfaceUniqueId);
        napi_create_int32(env, result, &ret);
        return ret;
    }
    sptr<IBufferProducer> producer = surface->GetProducer();
    if (!producer) {
        CLOGE("producer is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }

    result = adaptor->SetDmrSurface(producer);

    napi_create_int32(env, result, &ret);
    return ret;
}

void ResgisterDlnaPlayerAsyncExecuteCB(napi_env env, void* data)
{
    int result;
    struct ResgisterDlnaPlayerData* resgisterDlnaPlayerData = (struct ResgisterDlnaPlayerData*)data;
    result = SendDmrPlayer(resgisterDlnaPlayerData->object);
    resgisterDlnaPlayerData->result = result;
}

void ResgisterDlnaPlayerAsyncCallbackCompleteCB(napi_env env, napi_status status, void* data)
{
    struct ResgisterDlnaPlayerData* resgisterDlnaPlayerData = (struct ResgisterDlnaPlayerData*)data;
    napi_value callback = nullptr;
    napi_get_reference_value(env, resgisterDlnaPlayerData->callback, &callback);

    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);

    napi_value result = nullptr;
    napi_create_int32(env, resgisterDlnaPlayerData->result, &result);

    napi_value callbackState = nullptr;
    napi_call_function(env, undefined, callback, 1, &result, &callbackState);

    if (resgisterDlnaPlayerData->callback != nullptr) {
        napi_delete_reference(env, resgisterDlnaPlayerData->callback);
    }

    napi_delete_async_work(env, resgisterDlnaPlayerData->asyncWork);

    delete resgisterDlnaPlayerData;
}

void ResgisterDlnaPlayerAsyncPromiseCompleteCB(napi_env env, napi_status status, void* data)
{
    struct ResgisterDlnaPlayerData* resgisterDlnaPlayerData = (struct ResgisterDlnaPlayerData*)data;

    napi_value result = nullptr;
    napi_create_int32(env, resgisterDlnaPlayerData->result, &result);

    napi_resolve_deferred(env, resgisterDlnaPlayerData->deferred, result);

    if (resgisterDlnaPlayerData->callback != nullptr) {
        napi_delete_reference(env, resgisterDlnaPlayerData->callback);
    }

    napi_delete_async_work(env, resgisterDlnaPlayerData->asyncWork);

    delete resgisterDlnaPlayerData;
}

napi_value ResgisterDlnaPlayerAsync(napi_env env, napi_callback_info info)
{
    napi_value ret = nullptr;
    size_t argc = 2;
    napi_value argv[2];
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisArg, nullptr));

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype0));
    NAPI_ASSERT(env, valuetype0 == napi_object, "wrong argument type. Number expected.");

    auto resgisterDlnaPlayerData = new ResgisterDlnaPlayerData {
        .asyncWork = nullptr,
    };

    if (argc == 1) {
        napi_value promise = nullptr;
        napi_deferred deferred = nullptr;
        NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

        resgisterDlnaPlayerData->deferred = deferred;

        resgisterDlnaPlayerData->object = NAPI_ohos_rpc_getNativeRemoteObject(env, argv[0]);

        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "ResgisterDlnaPlayerAsyncPromise", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, ResgisterDlnaPlayerAsyncExecuteCB,
            ResgisterDlnaPlayerAsyncPromiseCompleteCB, (void*)resgisterDlnaPlayerData,
            &resgisterDlnaPlayerData->asyncWork);

        napi_queue_async_work(env, resgisterDlnaPlayerData->asyncWork);

        return promise;
    } else {
        napi_valuetype valuetype1;
        NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype1));
        if (valuetype1 != napi_function) {
            napi_throw_type_error(env, nullptr, "Callback function expected.");
            return nullptr;
        }

        resgisterDlnaPlayerData->object = OHOS::NAPI_ohos_rpc_getNativeRemoteObject(env, argv[0]);

        NAPI_CALL(env, napi_create_reference(env, argv[1], 1, &resgisterDlnaPlayerData->callback));

        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "ResgisterDlnaPlayerAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, ResgisterDlnaPlayerAsyncExecuteCB,
            ResgisterDlnaPlayerAsyncCallbackCompleteCB, (void*)resgisterDlnaPlayerData,
            &resgisterDlnaPlayerData->asyncWork);

        napi_queue_async_work(env, resgisterDlnaPlayerData->asyncWork);

        NAPI_CALL(env, napi_get_null(env, &ret));
        return ret;
    }
}

napi_value GetDlnaDeviceName(napi_env env, napi_callback_info info)
{
    std::string deviceName = "";
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_string_utf8(env, deviceName.c_str(), NAPI_AUTO_LENGTH, &ret);
        return ret;
    }
    adaptor->GetDlnaDeviceName(deviceName);
    CLOGD("napi_value::GetDlnaDeviceName %{public}s", deviceName.c_str());

    napi_create_string_utf8(env, deviceName.c_str(), NAPI_AUTO_LENGTH, &ret);
    return ret;
}

napi_value SetDlnaDeviceName(napi_env env, napi_callback_info info)
{
    CLOGW("SetDlnaDeviceName in");
    napi_value args[1];

    char strBuf[256];
    size_t strSize;
    size_t argc = 1;
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], strBuf, sizeof(strBuf), &strSize));
    std::string deviceName(strBuf, strSize);
    CLOGW("SetDlnaDeviceName:%{public}s", deviceName.c_str());

    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");

        napi_create_int32(env, result, &ret);
        return ret;
    }

    result = adaptor->SetDlnaDeviceName(deviceName);

    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value GetDmrState(napi_env env, napi_callback_info info)
{
    DmrStates dmrState = DmrStates::DMR_STATE_BUTT;
    napi_value ret = nullptr;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, -1, &ret);
        return ret;
    }
    adaptor->GetDmrState(dmrState);
    CLOGD("napi_value::GetDmrState %{public}d", dmrState);
    napi_create_int32(env, static_cast<int32_t>(dmrState), &ret);
    return ret;
}

napi_value GetVolume(napi_env env, napi_callback_info info)
{
    int32_t volume;
    int32_t maxVolume = -1;
    napi_value napiVolume;
    napi_value napiMaxVolume;
    napi_value ret = nullptr;
    napi_create_int32(env, -1, &napiVolume);
    napi_create_int32(env, -1, &napiMaxVolume);
    napi_create_array(env, &ret);
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        // napi_throw_error(env, "-1", "dlna service is null");
        CLOGW("dlna service is null");
        napi_set_element(env, ret, 0, napiVolume);
        napi_set_element(env, ret, 1, napiVolume);
        return ret;
    }
    adaptor->GetVolume(volume, maxVolume);
    CLOGI("dlna service is null%{public}d,%{public}d", volume, maxVolume);
    napi_create_int32(env, volume, &napiVolume);
    napi_create_int32(env, maxVolume, &napiMaxVolume);
    napi_set_element(env, ret, 0, napiVolume);
    napi_set_element(env, ret, 1, napiMaxVolume);
    return ret;
}

napi_value SetVolume(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg = nullptr;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
    int32_t volume;
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &volume));
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->SetVolume(volume);
    napi_create_int32(env, result, &ret);
    return ret;
}

napi_value AuthDeviceFail(napi_env env, napi_callback_info info)
{
    int32_t result = -1;
    napi_value ret;
    auto adaptor = GetAdaptor();
    if (!adaptor) {
        CLOGW("dlna service is null");
        napi_create_int32(env, result, &ret);
        return ret;
    }
    result = adaptor->AuthDeviceFail();
    napi_create_int32(env, result, &ret);
    return ret;
}

} // namespace CastEngineClient
} // namespace CastEngine
} // namespace OHOS
